package com.kx.base.util;

import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * 非对称加密：RSA算法的工具类
 * 1.生成密匙对
 * 2.公钥加密，私钥解密
 * 3.私钥加密，公钥解密
 *
 * @Author Sun
 * @Date 2021/7/25 17:21
 */
public class RSAUtil {

    /**
     * 私钥
     */
    private static String privateKeyString;

    /**
     * 公钥
     */
    private static String publicKeyString;


    /**
     * 生成随机的密匙对
     */
    public static void genKeyPair() {
        try {
            //生成RSA密匙对生成器
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            //初始化密钥对生成器，密钥大小为96~1024位
            keyPairGenerator.initialize(1024, new SecureRandom());
            //生成密匙对
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            //生成私匙
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            //生成公匙
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            //以base64的形式存储公钥与私钥
            privateKeyString = new String(Base64.encodeBase64(privateKey.getEncoded()));
            publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 公匙加密
     *
     * @param str       待加密的字符串
     * @param publicKey 加密使用的公钥
     * @return
     */
    public static String encryptPub(String str, String publicKey) {

        try {
            //把公匙由base64还原成字节数组
            byte[] bytes = Base64.decodeBase64(publicKey);
            //把公匙由字节数组还原成公匙对象
            PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(bytes));
            //用于加密的对象
            Cipher cipher = Cipher.getInstance("RSA");
            //指定密匙
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            //加密
            String result = Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 私密加密
     *
     * @param str        待加密的字符串
     * @param privateKey 私钥
     * @return
     */
    public static String encryptPri(String str, String privateKey) {

        try {
            //把私钥由base64还原为字节数组
            byte[] bytes = Base64.decodeBase64(privateKey);
            //把私钥由字节数组还原为私钥对象
            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(bytes));
            //加密对象
            Cipher cipher = Cipher.getInstance("RSA");
            //指定密钥
            cipher.init(Cipher.ENCRYPT_MODE, priKey);
            //进行加密
            String result = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
            return result;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 公钥解密
     *
     * @param str       带解密的密文
     * @param publicKey 解密使用的密匙
     * @return
     */
    public static String decryptPub(String str, String publicKey) {
        try {
            //拿到密文对应的字节数组
            byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
            //还原公钥
            byte[] keyByte = Base64.decodeBase64(publicKey);
            RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(keyByte));
            //RSA解密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, pubKey);
            //进行解密
            String result = new String(cipher.doFinal(inputByte));
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 私钥解密
     *
     * @param str        带解密的字符串
     * @param privateKey 解密使用的私钥
     * @return
     */
    public static String decryptPri(String str, String privateKey) {
        try {
            byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
            byte[] keyByte = Base64.decodeBase64(privateKey);
            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(keyByte));
            //RSA私钥解密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            //解密
            String result = new String(cipher.doFinal(inputByte));
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        genKeyPair();
        System.out.println("公钥为："+publicKeyString);
        System.out.println("私钥为："+privateKeyString);
        String info="Java真牛逼";
        String string1=encryptPri(info,privateKeyString);
        System.out.println("私钥加密之后为："+string1);
        String string2=decryptPub(string1,publicKeyString);
        System.out.println("公钥解密之后为："+string2);
        String string3=encryptPub(info,publicKeyString);
        System.out.println("公钥加密之后为："+string3);
        String string4=decryptPri(string3,privateKeyString);
        System.out.println("私钥解密之后为："+string4);
    }
}
